﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://wpfimageviewer.codeplex.com
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;
using System.Windows.Media.Imaging;
using AForge.Imaging;
using AForge.Imaging.Filters;
using BrainTechLLC;
using BrainTechLLC.ThreadSafeObjects;

namespace ImageAppDataModels
{
    public static class FindDuplicateImages
    {
        /// <summary>
        /// This routine finds duplicates within a file image collection, returning pairs of potential duplicates
        /// Duplicates are determined using (1) equivalent file size, (2) same MD5 hash
        /// </summary>
        /// <param name="collection"></param>
        /// <returns></returns>
        public static List<Pair<IFileImage, IFileImage>> FindDuplicates(this FileImageCollection collection, Action<string> updateStatus)
        {
            List<Pair<IFileImage, IFileImage>> dupes = new List<Pair<IFileImage, IFileImage>>();
            List<Pair<IFileImage, IFileImage>> dupesFirstPass = new List<Pair<IFileImage, IFileImage>>();
            List<IFileImage> originals = new List<IFileImage>(collection);
            List<IFileImage> now = new List<IFileImage>(originals);

            Dictionary<IFileImage, List<IFileImage>> filesSameSize = new Dictionary<IFileImage, List<IFileImage>>();

            if (updateStatus != null)
            {
                updateStatus(string.Format("Checking File Size for {0} files...", originals.Count));
            }
            for (int n = originals.Count - 1; n >= 0; n--)
            {
                IFileImage original = originals[n];

                for (int i = 0; i < n; i++)
                {
                    IFileImage compare = now[i];

                    if (original == compare || compare == null)
                        continue;

                    if (original.FileSize == compare.FileSize)
                    {
                        List<IFileImage> list;
                        if (!filesSameSize.TryGetValue(original, out list))
                        {
                            list = new List<IFileImage>();
                            filesSameSize.Add(original, list);
                        }
                        if (!list.Contains(compare) && !list.Contains(original) && !filesSameSize.ContainsKey(compare))
                        {
                            list.Add(compare);                            
                        }
                    }
                }
            }

            if (updateStatus != null)
            {
                updateStatus(string.Format("Comparing MD5 hash for files of the same size ({0} files)...", filesSameSize.Count));
            }
            foreach (var kvp in filesSameSize)
            {
                IFileImage original = kvp.Key;
                List<IFileImage> matches = kvp.Value;
                string checkSum = original.MD5Hash;

                for (int i = 0; i < matches.Count; i++)
                {
                    IFileImage compare = matches[i];
                    if (original != compare)
                    {
                        string otherChecksum = compare.MD5Hash;

                        if (checkSum.Equals(otherChecksum, StringComparison.Ordinal))
                        {
                            dupesFirstPass.Add(new Pair<IFileImage, IFileImage>(original, compare));
                        }
                    }
                }
            }

            Dictionary<IFileImage, IFileImage> alreadyFound = new Dictionary<IFileImage, IFileImage>();

            foreach (var v in dupesFirstPass)
            {
                IFileImage f1 = v.obj1;
                IFileImage f2 = v.obj2;

                if (alreadyFound.ContainsKey(f1) || alreadyFound.ContainsKey(f2))
                    continue;

                alreadyFound.Add(f1, f1);
                alreadyFound.Add(f2, f2);
                dupes.Add(v);
            }

            if (updateStatus != null)
            {
                updateStatus(string.Empty);
            }
            return dupes;
        }
    }
}
